namespace cxxcall
{

bool cb_data::_Reg_struct_ret ()
{
  if (this->rsize <= 4)
    return (true);

#ifdef __armhf__
  this->raddr = (void *)this->iarg[this->ianum++];
#else
  this->raddr = *(void **)this->curr;
  this->curr += sizeof (uintptr_t);
#endif
  return (false);
}

void* cb_data::xarg (size_t size, size_t align, bool integral, bool on_stack)
{
#ifdef __armhf__
  void *ptr;

  if (this->ianum + ((size + WSIZE - 1) / WSIZE) <= IARG_NUM)
    {
      this->ianum += cb_data::arg_size (size) / WSIZE;
      ptr = &this->iarg[this->ianum];
    }
  else
    {
      if (this->curr == (uintptr_t)&this->iarg[IARG_NUM])
        this->curr = (uintptr_t)&this->iarg[this->ianum] +
          cb_data::arg_size (size);
      else
        this->curr += cb_data::arg_size (size);

      this->ianum = IARG_NUM;
      ptr = (void *)this->curr;
    }

#  ifdef __ARMEL__
  return ((char *)ptr - cb_data::arg_size (size));
#  else
  return ((char *)ptr - (size < WSIZE ? size : cb_data::arg_size (size)));
#  endif
#else

  void *ret = (void *)this->curr;
#ifdef __ARMEL__
  this->curr += cb_data::arg_size (size);
#else
  this->curr += size < WSIZE ? size : cb_data::arg_size (size);
#endif
  return (ret);
#endif
}

long long cb_data::arg_ll ()
{
  const size_t ALIGN = alignof (long long);

#ifdef __armhf__
  if (this->ianum < IARG_NUM)
    this->ianum = (this->ianum + ALIGN / WSIZE - 1) &
      -(intptr_t)(ALIGN / WSIZE);
  else
#endif
  this->curr = (this->curr + ALIGN - 1) & -(intptr_t)ALIGN;

  return (*(long long *)this->xarg (sizeof (long long), ALIGN, true, true));
}

float cb_data::arg_f ()
{
#ifdef __armhf__
  if (this->fanum <= 15)
    return (this->fargs[this->fanum++]);

  void *ptr = (void *)this->curr;
  this->curr += sizeof (float);
  return (*(float *)ptr);
#else
  return (*(float *)this->xarg (sizeof (float), alignof (float), true, true));
#endif
}

double cb_data::arg_d ()
{
#ifdef __armhf__
  if (this->fanum % 2)
    ++this->fanum;
  if (this->fanum <= 14)
    {
      double ret = this->dargs[this->fanum / 2];
      this->fanum += 2;
      return (ret);
    }

  // XXX: Not sure if this is correct.
  void *ptr = (void *)this->curr;
  this->curr += sizeof (double);
  return (*(double *)ptr);
#else
  return (*(double *)this->xarg (sizeof (double),
                                 alignof (double), true, true));
#endif
}

} // namespace cxxcall
